package cn.buk.tms.eotms.service;

import cn.buk.api.pay.alipay.service.AlipayService;
import cn.buk.common.JsonResult;
import cn.buk.common.dto.CommonDto;
import cn.buk.common.flight.dto.FlightInfoDto;
import cn.buk.common.util.CheckUtil;
import cn.buk.common.util.DateUtil;
import cn.buk.common.util.FlightUtil;
import cn.buk.tms.common.constant.TmsOrderConstant;
import cn.buk.tms.common.dto.base.BaseOrderDto;
import cn.buk.tms.common.dto.base.BasePassengerDto;
import cn.buk.tms.common.dto.flight.BaseChangeOrderFlightDto;
import cn.buk.tms.eotms.dao.*;
import cn.buk.tms.eotms.dto.CarOrderDto;
import cn.buk.tms.eotms.dto.RoleInfo;
import cn.buk.tms.eotms.entity.*;
import cn.buk.tms.eotms.request.FlightOrderRequestDto;
import cn.buk.tms.eotms.response.*;
import cn.buk.tms.eotms.sc.SearchFlightOrderCriteria;
import cn.buk.tms.eotms.util.FlightOrderUtil;
import cn.buk.tms.eotms.util.FlightTicketUtil;
import cn.buk.tms.eotms.util.OrderUtil;
import cn.buk.tms.eotms.util.ValidationUtils;
import cn.buk.tms.exception.BaseException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

import static cn.buk.common.Constant.*;
import static cn.buk.tms.common.constant.TmsConstant.*;
import static cn.buk.tms.common.constant.TmsConstant.FLIGHT_TICKET_STATUS_EXCHANGED;
import static cn.buk.tms.common.constant.TmsOrderConstant.*;

/**
 * @author yfdai
 * @date 2015-3-19
 */
@Component
public class FlightOrderServiceImpl extends BaseAbstractService implements FlightOrderService {

  private final Logger logger = LogManager.getLogger(FlightOrderServiceImpl.class);

  @Autowired
  public FlightOrderServiceImpl(
          UserService userService,
          FlightOrderDao flightOrderDao,
                                RefundOrderDao refundOrderDao,
                                ChangeOrderDao changeOrderDao,
                                VasOrderDao vasOrderDao,
                                CarOrderDao carOrderDao,
                                OrderNoGenerator orderNoGenerator,
                                ProductDao productDao,
                                PaymentOrderDao paymentOrderDao,
                                AlipayService alipayService,
                                DetrDao detrDao) {
    this.userService = userService;
    this.flightOrderDao = flightOrderDao;
    this.refundOrderDao = refundOrderDao;
    this.changeOrderDao = changeOrderDao;
    this.vasOrderDao = vasOrderDao;
    this.carOrderDao = carOrderDao;
    this.orderNoGenerator = orderNoGenerator;
    this.productDao = productDao;
    this.paymentOrderDao = paymentOrderDao;
    this.alipayService = alipayService;
    this.detrDao = detrDao;
  }

  private final UserService userService;

  private final FlightOrderDao flightOrderDao;

  private final RefundOrderDao refundOrderDao;

  private final ChangeOrderDao changeOrderDao;

  private final VasOrderDao vasOrderDao;

  private final CarOrderDao carOrderDao;

  private final OrderNoGenerator orderNoGenerator;

  private final ProductDao productDao;

  private final PaymentOrderDao paymentOrderDao;

  private final AlipayService alipayService;

  private final DetrDao detrDao;

  @Override
  public List<FlightOrderResponseDto> searchFlightOrders(final int userId, final SearchFlightOrderCriteria sc) {
    final int ownerId = getOwnerIdByUserId(userId);
    final int customerId = getCustomerIdByUserId(userId);
    final int enterpriseId = getEnterpriseIdByUserId(userId);
    final String username = getUsernameByUserId(userId);

    Set<String> roles = getRolesByUserId(enterpriseId, userId);
    //管理员可以查看所有的员工信息
    //其它可以查看本部门所有的员工信息
    if (roles.contains(RoleInfo.ROLE_ADMIN)) {
      //管理员可以查看所有部门的订单
    } else if (roles.contains(RoleInfo.ROLE_MANAGER) || roles.contains(RoleInfo.ROLE_APPROVER)){
      //只能查看本部门的
      int deptId = getCustomerDeptIdByUserId(userId);
      sc.setDepartmentId(deptId);
    } else {
      //只能查看自己的订单
      sc.setOp1(username);
    }

    List<FlightOrder> flightOrders = flightOrderDao.searchOrder(ownerId, customerId, sc);

    if (flightOrders == null) {
      return new ArrayList<>();
    }

    List<FlightOrderResponseDto> dtos = new ArrayList<>();

    for (FlightOrder order : flightOrders) {
      dtos.add(FlightOrderResponseDto.createByFlightOrder(order));
    }

    return dtos;
  }

  @Override
  public CommonDto<FlightOrderResponseDto> searchFlightOrders2(int userId, SearchFlightOrderCriteria sc) {
    var datas = this.searchFlightOrders(userId, sc);

    CommonDto<FlightOrderResponseDto> commonDto = new CommonDto<>();
    commonDto.setDataList(datas);
    commonDto.setPage(sc.getPage());

    return commonDto;
  }

  @Override
  public FlightOrderResponseDto getFlightOrderDetail(final int userId, final int orderId) throws BaseException {
    final int ownerId = getOwnerIdByUserId(userId);
    final int customerId = getCustomerIdByUserId(userId);

    var flightOrder = flightOrderDao.getOrderById(orderId);

    if (flightOrder == null || flightOrder.getEnterpriseId() != ownerId || flightOrder.getCustomer().getId() != customerId) {
      logger.info("getFlightOrderDetail: {}, {}, {}, {}.", flightOrder.getEnterpriseId(), ownerId, flightOrder.getCustomer().getId(), customerId);
      throw new BaseException(30001, "订单不存在或无权访问");
    }

    return doGetFlightOrderDetail(flightOrder);
  }

  @Override
  public FlightOrderResponseDto getFlightOrderDetailByOrderNo(int userId, String orderNo) throws BaseException {
    final int ownerId = getOwnerIdByUserId(userId);
    var flightOrder = flightOrderDao.getOrderByOrderNo(ownerId, orderNo);
    logger.info("getFlightOrderDetailByOrderNo: {}, {}, {}, {}.", userId, ownerId, orderNo, flightOrder);
    if (flightOrder == null) {
      throw new BaseException(30001, "订单不存在或无权访问");
    }
    return this.getFlightOrderDetail(userId, flightOrder.getId());
  }

  private void processFlightTicketNos(List<FlightOrderPassengerDto> passengers) {
    for(FlightOrderPassengerDto psg: passengers) {
      final String originalTicketNo = psg.getTicketNo();
      if (FlightTicketUtil.verifyConnectionTicketNo(originalTicketNo)) {
        //保存的是连续票号
        String temp = originalTicketNo;
        final String balanceCode = temp.substring(0, 3);

        temp = temp.substring(3);
        if ("-".equalsIgnoreCase(temp.substring(0, 1))) {
          temp = temp.substring(1);
        }

        //前7位票号
        final String firstSevenTicketNo = temp.substring(0, 7);
        //后3位票号
        final String lastThreeTicketNo = temp.substring(7, 10);

        //连续票号的最后尾号
        final String lastConjTicketNo = temp.substring(11, 13);

        int count = 0;
        while (true) {
          int newPart = Integer.parseInt(lastThreeTicketNo) + count;
          String strNewPart = String.format("%03d", newPart);

          psg.getTicketNos().add(balanceCode + "-" + firstSevenTicketNo + strNewPart);

          if (strNewPart.substring(1, 3).equalsIgnoreCase(lastConjTicketNo)) {
            break;
          }




          count++;
          if (count >= 10) {
            break;
          }


        }


        temp = temp.substring(7);
      }
    }
  }

  /**
   * 校验预订的航班的起飞时间是否符合业务规则
   */
  private boolean validateFlightTime(final Date ddate, final String dtime) {
    //航班起飞日期已过的
    final Date today = DateUtil.getCurDate();

    if (ddate.getTime() < today.getTime()) {
      return false;
    } else if (ddate.getTime() == today.getTime()) {
      int hour = Integer.parseInt(dtime.substring(0, 2));
      return hour > DateUtil.getCurrentHour();
    }

    return true;
  }


  @Override
  public JsonResult createFlightOrder(final int userId, FlightOrderRequestDto dto) throws BaseException {
    validateFlightOrderRequest(userId, dto);

    //前端传过来的总金额(如果有附加产品，则附加产品的金额也包含在内）
    final double totalAmount = dto.getTotalAmount();
    //校验订单总额，calcCost计算后，dto.getTotalAmount()中就是计算出来的值了
    dto.calcCost();

    final String policyCode = StringUtils.trimToEmpty(dto.getPolicyCode());
    if (policyCode.length() > 0) {
      //选择了私有机票政策, 则发票类型不能选择“行程单”
      if (dto.getItineraryType() == 1) {
        throw new BaseException(20020, "私有运价产品不单开行程单");
      }
    } else {
      //选择的公布运价产品, 校验传送过来的价格是否有parValue(航司销售价）
      for (var priceDto : dto.getPrices()) {
        if (
                (priceDto.getPriceType() == PRICE_TYPE_ADULT && dto.getAdultCount() > 0)
                        || (priceDto.getPriceType() == PRICE_TYPE_CHILD && dto.getChildCount() > 0)
                        || (priceDto.getPriceType() == PRICE_TYPE_INFANT && dto.getInfantCount() > 0)
        ) {
          if (priceDto.getParValue() < 1) {
            throw new BaseException(20016, "公布运价产品的价格是必需的");
          }
        }
      }
    }

    if (dto.getItineraryType() == 4) {
      //检查是否选择了 服务产品
      int count = 0;
      for (String code : dto.getServiceProductCodes()) {
        if (code != null && code.trim().length() > 0) {
          count++;
        }
      }
      if (count == 0) {
        throw new BaseException(20021, "打包产品至少需要一项服务产品");
      }
    }

    //TODO 如果有服务产品，则需要创建子订单; 保险订单的费用只保存到保险单中;
    //TODO 暂时只考虑当前订单的所有航段、所有人都买保险的情况

    final int ownerId = getOwnerIdByUserId(userId);
    final int customerId = getCustomerIdByUserId(userId);
    final String username = getUsernameByUserId(userId);


    //查看是否有子订单
    List<VasOrder> subOrders = new ArrayList<>();
    double calcTotal = 0;
    for (String code : dto.getServiceProductCodes()) {
      TieInProduct tieInProduct = productDao.getTieInProductByCode(ownerId, code);
      if (tieInProduct == null) {
        throw new BaseException(20022, "服务产品不存在");
      }
      final int flightCount = dto.getFlights().size();
      final int passengerCount = dto.getPassengers().size();
      VasOrder vasOrder = createVasOrder(ownerId, username, customerId, tieInProduct, dto.getFlights(), dto.getPassengers());
      vasOrder.setPayType(dto.getPayType());
      vasOrder.setPrice(tieInProduct.getPrice());
      vasOrder.setDiscount(tieInProduct.getDiscount());
      vasOrder.setCount(flightCount * passengerCount);
//      vasOrder
      vasOrder.setTotal((vasOrder.getPrice() - vasOrder.getDiscount()) * vasOrder.getCount());

      subOrders.add(vasOrder);

      calcTotal += vasOrder.getTotal();
    }

    calcTotal += dto.getTotalAmount();

    if (policyCode.length() > 0) {
      //选择了私有运价，则订单中只能有成人, 并且不校验总价
      if (dto.getPsgCount() != dto.getAdultCount()) {
        throw new BaseException(20023, "私有运价产品只支持成人");
      }
    } else {
      //校验总价
      if (Math.abs(calcTotal - totalAmount) > 0.001) {
        throw new BaseException(20017, "订单价格校验不符(" + calcTotal + " !=  " + totalAmount + ")");
      }
    }

    //校验GP机票
    final int gpMode = userService.getGpMode(userId);
    if (gpMode == 2) {
      //当前客户仅能开GP机票
      dto.setGpTicket(1);
    } else if (gpMode == 0) {
      //当前客户不能开GP机票
      dto.setGpTicket(0);
    }


    // 将DTO中的内容转化到FlightOrder中
    FlightOrder info = convertTo(dto, ownerId, username, customerId);
    info.setStatus(FLIGHT_ORDER_STATUS_WAITING);

    try {
      int retCode = 0;
      if (subOrders.isEmpty()) {
        retCode = flightOrderDao.createOrder(info, username);
      } else {
        retCode = flightOrderDao.createBlanketOrder(userId, ownerId, customerId, info, subOrders, totalAmount);
      }
      var rs = CreateFlightOrderResponse.createResponse(retCode);
      rs.setOrderNo(info.getOrderNo());
      return rs;
    } catch (RuntimeException ex) {
      return CreateFlightOrderResponse.createResponse(-100, ex.getMessage());
    }

  }

  private void validateFlightOrderRequest(final int userId, FlightOrderRequestDto dto) throws BaseException {
    ValidationUtils.validate(dto);

    //校验prices中的价格每种类型只能有一个
    // DTO中的内容验证
    validateFlightOrder(dto);

    //检查该客户是否 测试模式 用户，如果是，则不校验航班起飞时间 yfdai 2021-11-02
    //航班时间需要在当前时间后1个小时
    var c = getCustomerByUserId(userId);
    if (c.getTestMode() == 0) {
      for (var fltDto : dto.getFlights()) {
        if (!validateFlightTime(fltDto.getDdate(), fltDto.getDtime())) {
          throw new BaseException(20014, "距离航班起飞时间太短");
        }
      }
    }

    //校验乘机人的手机号, 需要检验手机号不能相同
    Set<String> mobileSet = new HashSet<>();
    for(BasePassengerDto psgDto: dto.getPassengers()) {
      if (!CheckUtil.isMobileNo(psgDto.getMobile())) {
        throw new BaseException(20015, "手机号不能为空或不符合格式要求(" + psgDto.getName() + ", " + psgDto.getMobile() + ")");
      }
      mobileSet.add(psgDto.getMobile());
    }
    if (mobileSet.size() < dto.getPassengers().size()) {
      throw new BaseException(20015, "联系人手机号不能重复");
    }
  }

  /**
   * 在线支付机票订单
   * 1.确定该订单的付款方式为“现金”
   * 2.如果是“私有运价”订单，需要等后台核价完毕才能支付。
   * @param userId 用户id
   * @param id 订单id
   */
  @Override
  public JsonResult payFlightOrder(final int userId, final int id) throws BaseException {
    FlightOrderResponseDto orderDto = this.getFlightOrderDetail(userId, id);

    if (orderDto.getPayType() != PAY_TYPE_CASH) {
      return JsonResult.createJsonResult(-1, "该订单的付款方式非现金方式");
    }

    if (orderDto.getPriceCheckedStatus() != 1) {
      return JsonResult.createJsonResult(-2, "订单还在校验中，请稍等...");
    }

    final int enterpriseId = getEnterpriseIdByUserId(userId);
    final int ownerId = getOwnerIdByUserId(userId);

    //支付订单号
    // 10 - 支付宝支付
    // 20 - 表示微信支付
    String outTradeNo = "10"+ enterpriseId + "0" + userId + "0" + DateUtil.getCurDateTimeString("yyyyMMddHHmmss");
    //商户网站订单系统中唯一订单号，必填

    //订单名称
    final String subject = "机票订单付款";
    //必填
//    final int totalFee = (int)(orderDto.getTotalAmount() * 100);

    //保存支付订单
    PaymentOrder order = new PaymentOrder();
    order.setEnterpriseId(enterpriseId);
    order.setUserId(userId);
    order.setOwnerEnterpriseId(ownerId);
    order.setTradeNo(outTradeNo);
    order.setPrice(orderDto.getTotalAmount());
    order.setStatus(0);
    order.setMemo(subject);

    order.setOrderType(ORDER_TYPE_FLIGHT);
    order.setOrderId(id);

    JsonResult jsonResult = new JsonResult();

    jsonResult.setStatus("OK");
    int retCode = paymentOrderDao.create(order);

    String url = alipayService.createPaymentParams(outTradeNo, orderDto.getTotalAmount(), subject);

    logger.info(url);

    jsonResult.setUrl(url);

    return jsonResult;
  }

  /**
   * 请求将订单开票，步骤如下：
   * 1.判断付款方式
   *    现金：查看订单的收款状态，已收款才能提交为等待开票
   *    记账：查看公司是否需要审批
   * 2.判断公司是否需要审批
   *    不需要：提交为等待开票
   *    需要：查找当前部门的审批人人数
   * 3. 当前部门的审批人人数
   *    一个：选择该审批人为此订单的审批人
   *    多个：提示用户选择，选择审批人
   */
  @Override
  public JsonResult submitFlightOrder(final int userId, final int orderId) {
    final int ownerId = getOwnerIdByUserId(userId);
    final int customerId = getCustomerIdByUserId(userId);

    FlightOrder flightOrder = flightOrderDao.getOrderById(orderId);
    if (flightOrder == null
            || flightOrder.getEnterpriseId() != ownerId
            || flightOrder.getCustomer().getId() != customerId) {
      return JsonResult.createJsonResult(-1, "数据出错");
    }

    //国际机票订单需要等验价完成了才可以提交开票
    if (flightOrder.getIntlTicket() == 1
            && (flightOrder.getPriceChecked() == null || flightOrder.getPriceChecked() == 0)) {
      return JsonResult.createJsonResult(-100, "国际机票请等后台客服验价完成后再提交开票");
    }

    if (flightOrder.getPolicyCode() != null
            && (flightOrder.getPriceChecked() == null || flightOrder.getPriceChecked() == 0)) {
      //私有政策订单，要等待验价完毕后才允许提交开票
      return JsonResult.createJsonResult(-100, "国内优惠机票请等后台客服验价完成后再提交开票");
    }


    if (flightOrder.getPayType() == PAY_TYPE_CASH) {
      if (flightOrder.getPayStatus() == null || flightOrder.getPayStatus() == PAY_STATUS_WAITING) {
        return JsonResult.createJsonResult(-2, "请先支付");
      }
    } else {
      // 判断是否有信用额度
      final int customerEnterpriseId = getEnterpriseIdByUserId(userId);
      var bal = enterpriseDao.getEnterpriseBalance(ownerId, customerEnterpriseId);
      var availableAmount = bal.getCashBalance() + bal.getCreditLimit() - bal.getFrozenBalance();

      final int fee = (int)(flightOrder.getTotalAmount() * 100);

      if (availableAmount < fee) {
        return JsonResult.createJsonResult(-2, "账户余额不足");
      }
    }


    //可以提交开票了
    int retCode = flightOrderDao.submitFlightOrder(userId, orderId);

    return JsonResult.createJsonResult(retCode);
  }

  @Override
  public JsonResult approveFlightOrder(final int userId, final int orderId, final int denyCode, final String denyReason) {
    if (denyCode != 0 && (denyReason == null || denyReason.trim().length() < 3 )) {
      return JsonResult.createJsonResult(-1, "请输入拒绝的原因");
    }
    FlightOrder flightOrder = flightOrderDao.getOrderById(orderId);
    final int customerId = getCustomerIdByUserId(userId);
    if (customerId != flightOrder.getCustomer().getId()) {
      return JsonResult.createJsonResult(-2, "订单不存在");
    }

    //判断审批人是否有权审批
    //1.是指定的审批人
    final String username = getUsernameByUserId(userId);
    if (flightOrder.getApprovalInfo() != null &&
            username.equalsIgnoreCase(flightOrder.getApprovalInfo().getSpecifiedApprover())) {
      final int ownerId = getOwnerIdByUserId(userId);
      int retCode = flightOrderDao.approveFlightOrder(ownerId, customerId, username, orderId, denyCode, denyReason);
      return JsonResult.createJsonResult(retCode);
    } else {
      return JsonResult.createJsonResult(-1, "您不是指定审批人");
    }

    //TODO
    //2.如果不是，就是部门审批人
    //3.如果不是，就是主账户
    //4.都不是，提示无权审批


//    return null;
  }

  /**
   * 通过授权码获取订单详情（适用于通过短信链接访问订单详情）
   * @param authCode 授权码
   * @param ip
   */
  @Override
  public FlightOrderResponseDto getFlightOrderDetailByAuthCode(final int orderId, final String authCode, final String mobile, String ip) {
    if (authCode == null) {
      return createFlightOrderDto(-1, "缺少授权码");
    }

    AuthCodeInfo authCodeInfo = enterpriseDao.getAuthCodeInfo(authCode);
    if (authCodeInfo == null || authCodeInfo.getOrderId() != orderId) {
      return createFlightOrderDto(-2, "缺少有效授权码");
    }

    FlightOrder flightOrder = flightOrderDao.getOrderById(orderId);
    if (flightOrder == null) {
      return createFlightOrderDto(-3, "未找到相关订单");
    }

    flightOrderDao.appendFlightOrderLog(flightOrder.getEnterpriseId(), flightOrder.getId(), authCode + "(" + ip + ")");

    return doGetFlightOrderDetail(flightOrder);
  }

  private FlightOrderResponseDto doGetFlightOrderDetail(FlightOrder flightOrder) {
    var dto = FlightOrderResponseDto.createByFlightOrder(flightOrder);

    //TODO 一个乘机人有多张票号时，需要系统改进下
    processFlightTicketNos(dto.getPassengers());

    final int enterpriseId = flightOrder.getEnterpriseId();

    if (flightOrder.getBlanketOrderId() != null) {
      //有对应的总订单，查找是否有子订单
      List<BlanketOrder> subOrders = flightOrderDao.searchSubOrders(enterpriseId, flightOrder.getBlanketOrderId());
      if (subOrders.size() > 0) {
        for(BlanketOrder subOrder: subOrders) {
          if (subOrder.getActualOrderType() == TmsOrderConstant.ORDER_TYPE_INSURANCE) {
            //保险订单
            VasOrder vasOrder = vasOrderDao.getOrderById(subOrder.getActualOrderId());

            BaseOrderDto baseOrderDto = new BaseOrderDto();
            baseOrderDto.setId(vasOrder.getId());
            baseOrderDto.setOrderType(subOrder.getActualOrderType());
            baseOrderDto.setOrderNo(vasOrder.getOrderNo());
            baseOrderDto.setOrderStatus(vasOrder.getStatus());
            baseOrderDto.setTotal(vasOrder.getTotal());

            dto.getSubOrders().add(baseOrderDto);

            //将子订单的价格加入到totalAmount中
            dto.setTotalAmount(dto.getTotalAmount() + baseOrderDto.getTotal());
          }
        }
      }
    }

    //获取相关的 退票
    List<FlightRefundOrder> refundOrders = refundOrderDao.searchByFlightOrderNo(enterpriseId, flightOrder.getOrderNo());
    for(FlightRefundOrder refundOrder: refundOrders) {
      dto.getRefunds().add(RefundOrderResponseDto.createBy(refundOrder));
    }

    //获取相关的 改签
    List<FlightChangeOrder> changeOrders = changeOrderDao.searchByFlightOrderNo(enterpriseId, flightOrder.getOrderNo());
    for(FlightChangeOrder changeOrder: changeOrders) {
      dto.getChanges().add(ChangeOrderResponseDto.createdBy(changeOrder));
    }

    //获取相关的 服务
    List<VasOrder> vasOrders = vasOrderDao.searchByFlightOrderId(enterpriseId, flightOrder.getId());
    for(VasOrder vasOrder: vasOrders) {
      dto.getServices().add(VasOrderResponseDto.createByVasOrder(vasOrder));
    }

    //获取相关的租车
    List<RentalCarOrder> carOrders = carOrderDao.searchByFlightOrderId(enterpriseId, flightOrder.getId());
    for(RentalCarOrder carOrder: carOrders) {
      CarOrderDto carOrderDto = CarOrderDto.createByRentalCarOrder(carOrder);
      dto.getCars().add(carOrderDto);
    }

    doGetTickets(enterpriseId, dto);
    for(var t: dto.getTickets()) {
      t.setDportName(getAirportName(t.getDport()));
      t.setAportName(getAirportName(t.getAport()));
    }

    dto.processPassengerTicketStatus();

    dto.setPnrDetail(null);
    dto.setErrcode(0);

    return dto;
  }

  /**
   * 将DETR信息的改签结果合并到机票订单
   */
  private void doGetTickets(final int enterpriseId, final FlightOrderResponseDto dto) {
    if (dto.getTickets().isEmpty()) {
      return;
    }
    //将改签单的票号内容合并到dto.tickets中
    processTicketsChanged(enterpriseId, dto);

    //将退票单的内容合并到dto.tickets
    processTicketRefunded(enterpriseId, dto);

    var list = dto.getTickets().stream().sorted(
            Comparator.comparing(FlightOrderTicketDto::getPassengerName)
                    .thenComparing(FlightOrderTicketDto::getDdate)
                    .thenComparing(FlightOrderTicketDto::getDtime)
    ).collect(Collectors.toList());

    dto.setTickets(list);
  }


  /**
   * 将改签订单的内容合并到机票订单的票号内容中
   */
  private void processTicketsChanged(final int enterpriseId, final FlightOrderResponseDto dto) {
    for (ChangeOrderResponseDto changeDto : dto.getChanges()) {
      processTicketChanged(enterpriseId, dto, changeDto);
    }
  }

  /**
   * 合并改签单中的票号信息到机票订单的票号信息中。
   * @param dto 机票订单
   * @param changeDto 改签单
   */
  private void processTicketChanged(final int enterpriseId, final FlightOrderResponseDto dto, final ChangeOrderResponseDto changeDto) {
    for (BaseChangeOrderFlightDto flt : changeDto.getFlights()) {
      if (flt.getFltType() == FLIGHT_BEFORE_CHANGED) {
        //忽略改签单中标记为改签前的航班
        continue;
      }
      processTicketFlightChanged(enterpriseId, dto, changeDto, flt);
    }
  }

  /**
   * 合并改签单中的航班信息到机票订单
   * @param changeDto 改签单
   * @param flt 改签单中包含的改签后的航班信息
   */
  private void processTicketFlightChanged(final int enterpriseId, final FlightOrderResponseDto dto, final ChangeOrderResponseDto changeDto,
                                          BaseChangeOrderFlightDto flt) {
    var t = new FlightOrderTicketDto();
    t.setPassengerName(changeDto.getPsgName());

    final String ticketNo = changeDto.getNewTicketNo() == null ?
            changeDto.getBalanceCode() + "-" + changeDto.getTicketNo()
            :
            changeDto.getNewTicketNo();

    t.setTicketNo(ticketNo);

    t.setDport(flt.getDport());
    t.setAport(flt.getAport());
    t.setFlightNo(flt.getFlightNo());
    t.setDdate(DateUtil.formatDate(flt.getDdate(), DATE_YYYY_MM_DD));
    t.setSubclass(flt.getSubclass());
    t.setDtime(flt.getDtime());
    t.setAtime(flt.getAtime());
    t.setFltType(flt.getFltType());

//    if (changeDto.getDetrRefreshStatus() == 8 || changeDto.getDetrRefreshStatus() == 1) {
      //DETR状态已更新
      processChangedTicketDetrInfo(enterpriseId, t);
//    }

    dto.getTickets().add(t);

    try {
      applyFlightChangedStatus(dto.getTickets(), changeDto.getPsgName(), flt);
    } catch (ParseException e) {
      e.printStackTrace();
    }
  }

  private void processChangedTicketDetrInfo(final int enterpriseId, FlightOrderTicketDto t) {
    var detrInfo = detrDao.getByTicketNo(enterpriseId, t.getTicketNo().replace("-", ""));
    if (detrInfo == null) {
      return;
    }
    if ("2".equalsIgnoreCase(detrInfo.getIsUsed())) {
      t.setTicketStatus(FLIGHT_TICKET_STATUS_USED);
      return;
    }
    for (var detrFlt : detrInfo.getFlights()) {
      if (detrFlt.getDport().equalsIgnoreCase(t.getDport())
              && detrFlt.getAport().equalsIgnoreCase(t.getAport())
              && detrFlt.getSubClass().equalsIgnoreCase(t.getSubclass())
      ) {
        if (DETR_EXCHANGED.equalsIgnoreCase(detrFlt.getTicketStatus())) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_EXCHANGED);
        } else if (DETR_REFUNDED.equalsIgnoreCase(detrFlt.getTicketStatus())) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_REFUNDED);
        } else if (DETR_USED_FLOWN.equalsIgnoreCase(detrFlt.getTicketStatus())) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_USED);
        } else if (DETR_OPEN_FOR_USE.equalsIgnoreCase(detrFlt.getTicketStatus())) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_OPEN_FOR_USE);
        }
      }
    }
  }

  /**
   * 将退票单的内容合并到机票订单的票号内容中
   */
  private void processTicketRefunded(final int enterpriseId, final FlightOrderResponseDto dto) {
    for(var refundDto: dto.getRefunds()) {
      if (refundDto.getOrderStatus() != FlightRefundOrder.ORDER_STATUS_DONE) {
        continue;
      }
      for(var flt: refundDto.getFlights()) {
        for (var t : dto.getTickets()) {
          if (t.getPassengerName().equalsIgnoreCase(refundDto.getPsgName())
                  && flt.getDport().equalsIgnoreCase(t.getDport())
                  && flt.getAport().equalsIgnoreCase(t.getAport())
                  && flt.getFlightNo().equalsIgnoreCase(t.getFlightNo())
                  && DateUtil.formatDate(flt.getDdate(), DATE_YYYY_MM_DD).equalsIgnoreCase(t.getDdate())
                  && flt.getDtime().equalsIgnoreCase(t.getDtime())
          ) {
            t.setTicketStatus(FLIGHT_TICKET_STATUS_REFUNDED);
          }
        }
      }
    }
  }

  private void applyFlightChangedStatus(List<FlightOrderTicketDto> tickets, String psgName, BaseChangeOrderFlightDto f0) throws ParseException {
    for(var t: tickets) {
      if (t.getFltType() == FLIGHT_AFTER_CHANGED) {
        continue;
      }
      if (t.getPassengerName().equalsIgnoreCase(psgName)) {
        //此处用城市代码进行判断，比如上海有2个机场
        if (FlightUtil.getCityCode(f0.getDport()).equalsIgnoreCase(FlightUtil.getCityCode(t.getDport()))
                && FlightUtil.getCityCode(f0.getAport()).equalsIgnoreCase(FlightUtil.getCityCode(t.getAport()))
        ) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_EXCHANGED);
        }
      }
    }
  }


  private FlightOrderResponseDto createFlightOrderDto(int errCode, String errMsg) {
    FlightOrderResponseDto dto = new FlightOrderResponseDto();
    dto.setErrcode(errCode);
    dto.setErrmsg(errMsg);

    return dto;
  }

  private VasOrder createVasOrder(final int ownerId, final String username, final int customerId,
                                  final TieInProduct product, final List<FlightInfoDto> flights, final List<BasePassengerDto> passengers) {
    VasOrder info = new VasOrder();
    info.setProductType(product.getProductType());
    info.setProductCode(product.getProductCode());
    info.setProductName(product.getProductName());

    info.setCreateDate(DateUtil.getCurDate());
    info.setOperator(username);
    info.setEnterpriseId(ownerId);

    if (product.getProductType() == TieInProduct.INSURANCE) {
      // 保险
      info.setOrderNo(
              orderNoGenerator.generateOrderNo(ownerId, TmsOrderConstant.ORDER_NO_INSURANCE));
    } else {
      // 其它vas订单
      info.setOrderNo(orderNoGenerator.generateOrderNo(ownerId, TmsOrderConstant.ORDER_NO_VAS));
    }

    if (customerId == 0) {
      //散客
      info.setCustomerType(0);
    } else {
      Customer c = new Customer();
      c.setId(customerId);
      info.setCustomerType(1);
      info.setCustomer(c);
    }

    info.setPassengers(null);
    for (BasePassengerDto psgDto : passengers) {
      VasOrderPassenger psg = new VasOrderPassenger();
      BeanUtils.copyProperties(psgDto, psg);

      psg.setEnterpriseId(ownerId);
      psg.setOrder(info);

      info.getPassengers().add(psg);
    }

    info.setFlights(null);
    for (FlightInfoDto fltDto : flights) {
      VasOrderFlight vasOrderFlight = new VasOrderFlight();

      FlightInfo flt = vasOrderFlight.getFlight();
      BeanUtils.copyProperties(fltDto, flt);
      flt.setDepartureAirport(fltDto.getDport());
      flt.setArrivalAirport(fltDto.getAport());
      flt.setDepartureDate(DateUtil.formatDate(fltDto.getDdate(), "yyyy-MM-dd"));
      flt.setDepartureTime(fltDto.getDtime());
      flt.setArrivalTime(fltDto.getAtime());

      vasOrderFlight.setOrder(info);
      vasOrderFlight.setEnterpriseId(ownerId);
      vasOrderFlight.setId(0);

      info.getFlights().add(vasOrderFlight);
    }

    return info;
  }

  private FlightOrder convertTo(FlightOrderRequestDto dto, final int ownerId, final String username, final int customerId) {
    FlightOrder info = new FlightOrder();
    BeanUtils.copyProperties(dto, info, "flights", "passengers");

    info.setEnterpriseId(ownerId);

    //转化价格 begin
    OrderUtil.copyFlightOrderPrice(dto, info);
    //转化价格 end

    info.setCreateDate(DateUtil.getCurDate());
    info.setOperator(username);

    //生成机票订单号
    final String orderNo = orderNoGenerator.generateOrderNo(ownerId, TmsOrderConstant.ORDER_NO_FLIGHT_SELF_SERVICE);
    info.setOrderNo(orderNo);

    if (customerId == 0) {
      //散客
      info.setCustomerType(0);
    } else {
      info.setCustomerType(1);
      Customer customer = new Customer();
      customer.setId(customerId);

      info.setCustomer(customer);
    }

    info.setFlights(null);
    info.setPassengers(null);

    // 航段信息
    OrderUtil.copyFlightOrderFlights(dto, info);
    processOrderAirportName(info);

    // 乘机人信息
    OrderUtil.copyFlightOrderPassengers(dto, info);


    //处理到达时间+1的问题
    for(FlightOrderFlight flight: info.getFlights()) {
      final FlightInfo fltInfo = flight.getFlight();
      final String atime = fltInfo.getArrivalTime();
      if (FlightOrderUtil.validateFlightTime(atime) && atime.length() == 6 && atime.contains("+1")) {
        try {
          Date ddate = DateUtil.convertToDate(fltInfo.getDepartureDate(), "yyyy-MM-dd");
          Date adate = DateUtil.addDays(ddate, 1);
          fltInfo.setArrivalDate(DateUtil.formatDate(adate, "yyyy-MM-dd"));
          fltInfo.setArrivalTime(atime.substring(0, 4));
        } catch (ParseException e) {
          logger.error(e.getMessage());
        }
      }
    }

    info.setAdultCount(dto.getAdultCount());
    info.setChildCount(dto.getChildCount());
    info.setInfantCount(dto.getInfantCount());

    return info;
  }

  /**
   * 校验机票预订单的请求
   */
  private void validateFlightOrder(FlightOrderRequestDto dto) throws BaseException {
    if (dto.getFlights().isEmpty() || dto.getPassengers().isEmpty()) {
      throw new BaseException(20018, "航段信息和乘机人信息都必需有");
    }

    for (FlightInfoDto info : dto.getFlights()) {
      if (info == null) {
        throw new BaseException(20018, "航段信息和乘机人信息都必需有");
      }

      validateFlightInfo(info);

      if (info.getDtime() != null) {
        info.setDtime(info.getDtime().replace(":", ""));
      }
      if (info.getAtime() != null) {
        info.setAtime(info.getAtime().replace(":", ""));
      }

      if (!FlightOrderUtil.validateFlightTime(info.getDtime())) {
        throw new BaseException(20005, "出发时间不能为空或格式不正确");
      }
      if (!FlightOrderUtil.validateFlightTime(info.getAtime())) {
        throw new BaseException(20006, "到达时间不能为空或格式不正确");
      }

      if (info.getDterm() != null && info.getDterm().length() > 2) {
        throw new BaseException(20000, "出发航站楼(" + info.getDterm() + ")" + "不符合格式要求");
      }
      if (info.getAterm() != null && info.getAterm().length() > 2) {
        throw new BaseException(20000, "到达航站楼(" + info.getAterm() + ")" + "不符合格式要求");
      }
    }

    final String policyCode = StringUtils.trimToEmpty(dto.getPolicyCode());
    for (BasePassengerDto psg : dto.getPassengers()) {
      if (psg.getName() != null && psg.getName().trim().length() > 0
              && psg.getFirstName() != null && psg.getFirstName().trim().length() > 0) {
        throw new BaseException(20007, "name和firstName, lastName不能同时填写");
      }
      if ((psg.getName() == null || psg.getName().trim().length() == 0) &&
              (psg.getFirstName() == null || psg.getFirstName().trim().length() == 0)) {
        throw new BaseException(20008, "姓名或姓和名不能都为空或不符合格式要求");
      }
      if (psg.getName() != null && psg.getName().length() < 2) {
        throw new BaseException(20008, "姓名或姓和名不能都为空或不符合格式要求");
      }
      if (psg.getFirstName() != null && psg.getFirstName().trim().length() < 2) {
        throw new BaseException(20008, "姓名或姓和名不能都为空或不符合格式要求");
      }

      if (psg.getPsgType() == null) {
        throw new BaseException(20009, "乘机人类型不符合要求");
      }

      //判断证件类型
      if (psg.getIdType() == null || psg.getIdType() == 0) {
        throw new BaseException(20010, "请选择证件类型");
      }
      final String idNo = StringUtils.trimToEmpty(psg.getIdNo());
      if (psg.getIdType() == ID_TYPE_NI && idNo.length() != 18) {
        throw new BaseException(20011, "身份证号不符合格式要求");
      }
      if (idNo.length() < 2 && psg.getPsgType() != PSG_TYPE_INF) {
        throw new BaseException(20012, "证件号不符合格式要求");
      }
      if (idNo.length() > 20) {
        throw new BaseException(20012, "证件号不符合格式要求");
      }

      if (psg.getIdType() != ID_TYPE_NI && policyCode.length() > 0) {
        throw new BaseException(20000, "私有政策暂仅支持身份证(" + psg.getName() + ")");
      }

      if (psg.getMobile() != null && psg.getMobile().trim().length() > 11) {
        throw new BaseException(20015, "手机号长度不能超过11位");
      }
    }

    if (dto.getItineraryType() > 0 && dto.getAddress().length() < 5) {
      //需要邮寄行程单或发票，则需要录入地址
      throw new BaseException(20019, "邮寄地址, 地址长度不能小于5");
    }

    //校验价格类型，每种类型只能有一个
    int adultPrice = 0, childPrice = 0, infantPrice = 0;
    for (var p : dto.getPrices()) {
      switch (p.getPriceType()) {
        case 0:
          adultPrice++;
          break;
        case 1:
          childPrice++;
          break;
        case 2:
          infantPrice++;
          break;
        default:
          break;
      }
    }
    if (adultPrice > 1 || childPrice > 1 || infantPrice > 1) {
      throw new BaseException(20013, "航班价格不符合要求");
    }
    if (adultPrice == 0 && childPrice == 0) {
      throw new BaseException(20013, "航班价格不符合要求");
    }
  }
}
